﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using SFP;
using SFP.Animation2D;
using SFP.Audio;
using SFP.Input;
using SFP.CollisionDetection;
using HamsterDelivery.LevelStructure;

namespace HamsterDelivery
{
    public class Player : LayerObject
    {
        /* Constants */
        const double JUMP_VELOCITY = 500;
        const double MAX_FALL_VELOCITY = 500;

        const double MAX_WALK_VELOCITY = 250;
        const double WALK_ACCELERATION = 1;
        const double WALK_FADE_OUT = 1;

        const double ANIMATION_SPEED = 80;

        /* Player */
        public double VelocityX { set; get; }
        public double VelocityY { set; get; }
        public bool OnGround { set; get; }
        public bool Jump { set; get; }
        public bool Walking { set; get; }
        public bool GroundCollisionFound { set; get; }

        /* Image-Animators */
        ImageSequencer walkingRightSeq;
        ImageSequencer walkingLeftSeq;
        ImageSequencer activeWalkingSeq;

        /* Game */
        HamsterGame game;

        public Player(int x, int y, HamsterGame game)
        {
            X = x;
            Y = y;
            this.game = game;
        }

        public void setUpAnimations()
        {
            walkingRightSeq = new ImageSequencer(new Bitmap[] { Resources.B_WALKING_RIGHT_1, Resources.B_WALKING_RIGHT_2, Resources.B_WALKING_RIGHT_3 },
                                               ANIMATION_SPEED, new Point(intValue(X), intValue(Y)));
            walkingLeftSeq = new ImageSequencer(new Bitmap[] { Resources.B_WALKING_LEFT_1, Resources.B_WALKING_LEFT_2, Resources.B_WALKING_LEFT_3 },
                                               ANIMATION_SPEED, new Point(intValue(X), intValue(Y)));
            activeWalkingSeq = walkingRightSeq;
        }

        public void reset(int x, int y)
        {
            X = x;
            Y = y;

            VelocityX = 0;
            VelocityY = 0;

            Jump = false;
            OnGround = false;
            Walking = false;
            GroundCollisionFound = false;
            activeWalkingSeq = walkingRightSeq;
        }

        private int intValue(double value)
        {
            return (int)Math.Round(value, 0, MidpointRounding.AwayFromZero);
        }

        public Rectangle GetRectangle()
        {
            int Width = Resources.B_STANDING_FACE_RIGHT.Width;
            int Height = Resources.B_STANDING_FACE_LEFT.Height;

            return new Rectangle(intValue(X), intValue(Y), Width, Height);
        }

        public void update(double time, KeyboardState keyStates)
        {
            /* Start or stop walking */
            walk(time, keyStates);

            /* Start or stop jump */
            jump(time, keyStates);

            /* Gravity */
            applyGravity(time);

            /* New position */
            moveToNewPosition(time);

            /* Collision detection/reaction */
            collisionDetection(time);

            /* Set position for animations */
            walkingRightSeq.Position = new Point(intValue(X), intValue(Y));
            walkingLeftSeq.Position = new Point(intValue(X), intValue(Y));
        }

        public void walk(double time, KeyboardState keyStates)
        {
            if (keyStates.isKeyDown(Keys.D) && keyStates.isKeyUp(Keys.A))
            {
                Walking = true;

                walkingRightSeq.animate(time, true);
                walkingLeftSeq.resetSequence();
                activeWalkingSeq = walkingRightSeq;

                VelocityX += WALK_ACCELERATION * time;
                if (VelocityX > MAX_WALK_VELOCITY) VelocityX = MAX_WALK_VELOCITY;
            }
            else if (keyStates.isKeyDown(Keys.A) && keyStates.isKeyUp(Keys.D))
            {
                Walking = true;

                walkingLeftSeq.animate(time, true);
                walkingRightSeq.resetSequence();
                activeWalkingSeq = walkingLeftSeq;

                VelocityX -= WALK_ACCELERATION * time;
                if (VelocityX < -MAX_WALK_VELOCITY) VelocityX = -MAX_WALK_VELOCITY;
            }
            else
            {
                Direction hDirection = hMoveDirection();

                if (hDirection == Direction.Right)
                {
                    walkingRightSeq.animate(time, true);
                    walkingLeftSeq.resetSequence();

                    VelocityX -= WALK_FADE_OUT * time;

                    if (VelocityX < 0)
                    {
                        VelocityX = 0;
                    }
                }
                else if (hDirection == Direction.Left)
                {
                    walkingLeftSeq.animate(time, true);
                    walkingRightSeq.resetSequence();

                    VelocityX += WALK_FADE_OUT * time;

                    if (VelocityX > 0)
                    {
                        VelocityX = 0;
                    }
                }
                else if (hDirection == Direction.None)
                {
                    walkingRightSeq.resetSequence();
                    walkingLeftSeq.resetSequence();
                    Walking = false;
                }
            }
        }

        public void jump(double time, KeyboardState keyStates)
        {
            if (keyStates.isKeyDownNotPressed(Keys.Space))
            {
                if (OnGround)
                {
                    VelocityY = JUMP_VELOCITY + time / 2;
                    Jump = true;
                    OnGround = false;
                    AudioPlayer.play(Resources.A_JUMP_SOUND, true);
                }
            }
            if (keyStates.isKeyUp(Keys.Space))
            {
                if (Jump && VelocityY > 0) VelocityY = 0;
                Jump = false;
            }
        }

        public void applyGravity(double time)
        {
            if (!OnGround) VelocityY -= Level.GRAVITY * time / 1000;
            else VelocityY = 0;
            if (VelocityY < -MAX_FALL_VELOCITY) VelocityY = -MAX_FALL_VELOCITY;
        }

        public void moveToNewPosition(double time)
        {
            LayerManager layerManager = game.Level.LayerManager;

            X += VelocityX * time / 1000;
            Y -= VelocityY * time / 1000;

            if (X < 0)
            {
                X = 0;
                VelocityX = 0;
            }
            else if (X + activeWalkingSeq.Width > game.resolution.Width)
            {
                X = game.resolution.Width - activeWalkingSeq.Width;
                VelocityX = 0;
            }

            int resolutionHalfWidth = game.resolution.Width / 2;
            if (!layerManager.isXAtEnd() && X > resolutionHalfWidth)
            {
                X = resolutionHalfWidth;
                layerManager.scrollX(VelocityX * (time / 1000));
            }
            if (!layerManager.isXAtBeginn() && X < resolutionHalfWidth)
            {
                X = resolutionHalfWidth;
                layerManager.scrollX(VelocityX * (time / 1000));
            }

            int resolutionHalfHeight = game.resolution.Height / 2;
            if (!layerManager.isYAtEnd() && Y < resolutionHalfHeight && !OnGround)
            {
                Y = resolutionHalfHeight;
                layerManager.scrollY(VelocityY * (time / 1000));
            }
            if (!layerManager.isYAtBeginn() && Y > resolutionHalfHeight && !OnGround)
            {
                Y = resolutionHalfHeight;
                layerManager.scrollY(VelocityY * (time / 1000));
            }

            if (Y > game.resolution.Height)
            {
                game.Level.LayerManager.scrollToOrigin();
                reset((int)game.Level.startBlock.X, (int)game.Level.startBlock.Y);
            }
        }

        public void collisionDetection(double time)
        {
            GroundCollisionFound = false;

            game.Level.processLevelCollisions(GetRectangle(), vMoveDirection(), hMoveDirection());

            if (!GroundCollisionFound) OnGround = false;
        }

        public Direction vMoveDirection()
        {
            if (VelocityY > 0) return Direction.Up;
            else if (VelocityY < 0) return Direction.Down;

            return Direction.None;
        }

        public Direction hMoveDirection()
        {
            if (VelocityX < 0) return Direction.Left;
            else if (VelocityX > 0) return Direction.Right;

            return Direction.None;
        }

        public override void draw(Graphics g)
        {
            if (!OnGround)
            {
                if (activeWalkingSeq == walkingLeftSeq)
                    g.DrawImage(Resources.B_JUMP_LEFT, intValue(X), intValue(Y - Resources.B_JUMP_LEFT.Height));
                else
                    g.DrawImage(Resources.B_JUMP_RIGHT, intValue(X), intValue(Y - Resources.B_JUMP_RIGHT.Height));
            }
            else if (Walking)
            {
                activeWalkingSeq.draw(g);
            }
            else
            {
                if (activeWalkingSeq == walkingRightSeq)
                    g.DrawImage(Resources.B_STANDING_FACE_RIGHT, intValue(X), intValue(Y - Resources.B_STANDING_FACE_RIGHT.Height));
                else
                    g.DrawImage(Resources.B_STANDING_FACE_LEFT, intValue(X), intValue(Y - Resources.B_STANDING_FACE_LEFT.Height));
            }
        }
    }
}
